﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using aoprinter.Model;
using System.Xml.Linq;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace aoprinter.IO
{
    /// <summary>
    /// get/set print report set from XML
    /// </summary>
    internal class ReportIO
    {
        static private List<ModRegionStyle> _lstModRegion;
        static internal ModReport Read(string xmlPath)
        {
            XDocument xd = XDocument.Load(xmlPath);
            _lstModRegion = new List<ModRegionStyle>();
            IEnumerable<XElement> ieRootXml = from x in xd.Elements("report") select x;
            if (ieRootXml == null || ieRootXml.Count() < 1) return null;
            return ReadReport(ieRootXml.ElementAt(0));
        }
        static private ModReport ReadReport(XElement rootXml)
        {
            ModReport modReport = new ModReport();
            if (rootXml.Attribute("reportID") != null) modReport.ReportID = rootXml.Attribute("reportID").Value;
            if (rootXml.Attribute("reportName") != null) modReport.ReportName = rootXml.Attribute("reportName").Value;
            if (rootXml.Attribute("location") != null && rootXml.Attribute("location").Value.Split(',').Length == 2)
            {
                float x = -1;
                float.TryParse(rootXml.Attribute("location").Value.Split(',')[0], out x);
                float y = -1;
                float.TryParse(rootXml.Attribute("location").Value.Split(',')[0], out y);
                modReport.Location = new PointF(x, y);
            }
            if (rootXml.Attribute("position") != null)
            {
                EnmPosition ep = EnmPosition.Suspend;
                if (Enum.TryParse(rootXml.Attribute("position").Value, out ep))
                {
                    modReport.Position = ep;
                }
            }
            if (rootXml.Attribute("scaleX") != null)
            {
                float f = 0;
                if (float.TryParse(rootXml.Attribute("scaleX").Value, out f))
                {
                    modReport.ScaleX = f;
                }
            }
            if (rootXml.Attribute("scaleY") != null)
            {
                float f = 0;
                if (float.TryParse(rootXml.Attribute("scaleY").Value, out f))
                {
                    modReport.ScaleY = f;
                }
            }
            if (rootXml.Attribute("size") != null && rootXml.Attribute("size").Value.Split(',').Length == 2)
            {
                float w = 0;
                float.TryParse(rootXml.Attribute("size").Value.Split(',')[0], out w);
                float h = 0;
                float.TryParse(rootXml.Attribute("size").Value.Split(',')[1], out h);
                modReport.Size = new SizeF(w, h);
            }
            IEnumerable<XElement> iepageHFXml = from x in rootXml.Elements("pageHead") select x;
            if (iepageHFXml != null && iepageHFXml.Count() > 0 && iepageHFXml.ElementAt(0) != null)
            {
                modReport.Head = ReadHeadOrFoot(iepageHFXml.ElementAt(0), new ModPageHeader());
            }
            IEnumerable<XElement> iePageXml = from x in rootXml.Elements("page") select x;
            if (iePageXml != null && iePageXml.Count() > 0)
            {
                foreach (XElement xPageXml in iePageXml)
                {
                    modReport.Pages.Add(ReadPages(xPageXml));
                }
            }
            IEnumerable<XElement> iePageFootXml = from x in rootXml.Elements("pageFoot") select x;
            if (iePageFootXml != null && iePageFootXml.Count() > 0 && iePageFootXml.ElementAt(0) != null)
            {
                modReport.Foot = ReadHeadOrFoot(iePageFootXml.ElementAt(0), new ModPageFooter());
            }
            return modReport;
        }
        static private ModPage ReadPages(XElement pageXml)
        {
            ModPage modPage = new ModPage();
            if (pageXml.Attribute("location") != null && pageXml.Attribute("location").Value.Split(',').Length == 2)
            {
                float x = -1;
                float.TryParse(pageXml.Attribute("location").Value.Split(',')[0], out x);
                float y = -1;
                float.TryParse(pageXml.Attribute("location").Value.Split(',')[1], out y);
                modPage.Location = new PointF(x, y);
            }
            if (pageXml.Attribute("noPageHeader") != null)
            {
                bool noPageHeader = false;
                if (bool.TryParse(pageXml.Attribute("noPageHeader").Value, out noPageHeader))
                {
                    modPage.NoPagHeader = noPageHeader;
                }
            }
            if (pageXml.Attribute("noPageFooter") != null)
            {
                bool noPageFooter = false;
                if (bool.TryParse(pageXml.Attribute("noPageFooter").Value, out noPageFooter))
                {
                    modPage.NoPageFooter = noPageFooter;
                }
            }
            if (pageXml.Attribute("noPageNum") != null)
            {
                bool noPageNum = false;
                if (bool.TryParse(pageXml.Attribute("noPageNum").Value, out noPageNum))
                {
                    modPage.NoPageNum = noPageNum;
                }
            }
            if (pageXml.Attribute("isCustom") != null)
            {
                bool isCustom = false;
                if (bool.TryParse(pageXml.Attribute("isCustom").Value, out isCustom))
                {
                    modPage.IsCustom = isCustom;
                }
            }
            if (pageXml.Attribute("bindID") != null) modPage.BindID = pageXml.Attribute("bindID").Value;
            if (pageXml.Attribute("memberID") != null) modPage.MemberID = pageXml.Attribute("memberID").Value;
            if (pageXml.Attribute("position") != null)
            {
                EnmPosition ep = EnmPosition.Suspend;
                if (Enum.TryParse(pageXml.Attribute("position").Value, out ep))
                {
                    modPage.Position = ep;
                }
            }
            if (pageXml.Attribute("size") != null && pageXml.Attribute("size").Value.Split(',').Length == 2)
            {
                float w = 0;
                float.TryParse(pageXml.Attribute("size").Value.Split(',')[0], out w);
                float h = 0;
                float.TryParse(pageXml.Attribute("size").Value.Split(',')[1], out h);
                modPage.Size = new SizeF(w, h);
            }
            IEnumerable<XElement> ieCase = from x in pageXml.Elements() select x;
            foreach (XElement xml in ieCase)
            {
                modPage.Blocks.Add(ReadCase(xml, new ModCase()));
            }
            return modPage;
        }
        static private dynamic ReadHeadOrFoot(XElement pageHFXml, dynamic modPageHF)
        {
            modPageHF = ReadCase(pageHFXml, modPageHF);
            IEnumerable<XElement> iePageNum = from x in pageHFXml.Elements("pageNum") select x;
            if (iePageNum != null && iePageNum.Count() > 0 && iePageNum.ElementAt(0) != null)
            {
                modPageHF.PageNum = ReadPageNum(iePageNum.ElementAt(0));
            }
            return modPageHF;
        }
        static private ModPageNum ReadPageNum(XElement pageNumXml)
        {
            ModPageNum modPageNum = new ModPageNum();
            if (pageNumXml.Attribute("pageFormat") != null) modPageNum.PageFormat = pageNumXml.Attribute("pageFormat").Value;
            if (pageNumXml.Attribute("location") != null && pageNumXml.Attribute("location").Value.Split(',').Length == 2)
            {
                float x = -1;
                float.TryParse(pageNumXml.Attribute("location").Value.Split(',')[0], out x);
                pageNumXml.Attribute("location").Value.Split(',')[0] = x.ToString();
                float y = -1;
                float.TryParse(pageNumXml.Attribute("location").Value.Split(',')[1], out y);
                modPageNum.Location = new PointF(x, y);
            }
            modPageNum.TextStyle = ReadTextStyle(FindStyleXml("textStyle", "textStyleID", pageNumXml));
            return modPageNum;
        }

        static private dynamic FindList(XElement xml, string styleID)
        {
            switch (xml.Name.ToString())
            {
                case "regionStyle":
                    return _lstModRegion.Find(x => x.GetType() == typeof(ModRegionStyle) && x.StyleID == styleID);
                case "lineStyle":
                    return _lstModRegion.Find(x => x.GetType() == typeof(ModLineStyle) && x.StyleID == styleID);
                case "textStyle":
                    return _lstModRegion.Find(x => x.GetType() == typeof(ModTextStyle) && x.StyleID == styleID);
                default:
                    return null;
            }
        }
        static private dynamic ReadBaseStyle(XElement xml, dynamic dyn)
        {
            if (xml == null) return null;
            if (xml.Attribute("brushType") != null)
            {
                EnmBrush enmb = EnmBrush.Suspend;
                if (Enum.TryParse(xml.Attribute("brushType").Value, out enmb))
                {
                    dyn.BrushType = enmb;
                }
            }
            if (xml.Attribute("styleID") != null) dyn.StyleID = xml.Attribute("styleID").Value;
            if (xml.Attribute("hatchBrushStyle") != null)
            {
                HatchStyle hs = HatchStyle.BackwardDiagonal;
                if (Enum.TryParse(xml.Attribute("hatchBrushStyle").Value, out hs))
                {
                    dyn.HatchBrushStyle = hs;
                }
            }
            IEnumerable<XElement> ieBlend = from x in xml.Elements("blend") select x;
            dyn.Colors = new Color[ieBlend.Count()];
            dyn.Points = new PointF[ieBlend.Count()];
            int i = 0;
            foreach (XElement xBlend in ieBlend)
            {
                if (xBlend.Attribute("color") != null)
                {
                    string[] colorStr = xBlend.Attribute("color").Value.Split(',');
                    if (colorStr.Length == 4)
                    {
                        int alpha = 0;
                        int.TryParse(colorStr[0], out alpha);
                        int red = 0;
                        int.TryParse(colorStr[1], out red);
                        int green = 0;
                        int.TryParse(colorStr[2], out green);
                        int blue = 0;
                        int.TryParse(colorStr[3], out blue);
                        dyn.Colors[i] = Color.FromArgb(alpha, red, green, blue);
                    }
                    else if (colorStr.Length == 3)
                    {
                        int red = 0;
                        int.TryParse(colorStr[0], out red);
                        int green = 0;
                        int.TryParse(colorStr[1], out green);
                        int blue = 0;
                        int.TryParse(colorStr[2], out blue);
                        dyn.Colors[i] = Color.FromArgb(red, green, blue);
                    }
                    else if (xBlend.Attribute("color").Value.Split(',').Length == 1)
                    {
                        dyn.Colors[i] = Color.FromName(xBlend.Attribute("color").Value);
                    }
                }
                if (xBlend.Attribute("point") != null)
                {
                    if (xBlend.Attribute("point").Value.Split(',').Length == 2)
                    {
                        int x = 0;
                        int.TryParse(xBlend.Attribute("point").Value.Split(',')[0], out x);
                        int y = 0;
                        int.TryParse(xBlend.Attribute("point").Value.Split(',')[1], out y);
                        dyn.Points[i] = new PointF(x, y);
                    }
                }
                i++;
            }
            return dyn;
        }
        static private ModRegionStyle ReadRegionStyle(XElement regionXml)
        {
            if (regionXml == null) return null;
            ModRegionStyle modRegionStyle = FindList(regionXml, regionXml.Attribute("styleID").Value);
            if (modRegionStyle != null) return modRegionStyle;
            modRegionStyle = new ModRegionStyle();
            _lstModRegion.Add(ReadBaseStyle(regionXml, modRegionStyle));
            return FindList(regionXml, modRegionStyle.StyleID);
        }
        static private ModLineStyle ReadLineStyle(XElement lineXml)
        {
            if (lineXml == null) return null;
            ModLineStyle modLineStyle = FindList(lineXml, lineXml.Attribute("styleID").Value);
            if (modLineStyle != null) return modLineStyle;
            modLineStyle = new ModLineStyle();
            modLineStyle = ReadBaseStyle(lineXml, modLineStyle);
            if (lineXml.Attribute("lineStyle") != null)
            {
                DashStyle ds = DashStyle.Solid;
                if (Enum.TryParse(lineXml.Attribute("lineStyle").Value, out ds))
                {
                    modLineStyle.LineStyle = ds;
                }
            }
            if (lineXml.Attribute("lineType") != null)
            {
                EnmLineType enmlt = EnmLineType.SingleLine;
                if (Enum.TryParse(lineXml.Attribute("lineType").Value, out enmlt))
                {
                    modLineStyle.LineType = enmlt;
                }
            }
            IEnumerable<XElement> iePara = from x in lineXml.Elements("parameter") select x;
            foreach (XElement xml in iePara)
            {
                if (xml.Attribute("key") != null && xml.Attribute("value") != null)
                {
                    if (modLineStyle.Parameters.Keys.Contains(xml.Attribute("key").Value)) continue;
                    modLineStyle.Parameters.Add(xml.Attribute("key").Value, xml.Attribute("value").Value);
                }
            }
            if (lineXml.Attribute("width") != null)
            {
                float w = 0.5f;
                if (float.TryParse(lineXml.Attribute("width").Value, out w))
                {
                    modLineStyle.Width = w;
                }
            }
            _lstModRegion.Add(modLineStyle);
            return modLineStyle;
        }
        static private ModTextStyle ReadTextStyle(XElement textXml)
        {
            if (textXml == null) return null;
            ModTextStyle modTextStyle = FindList(textXml, textXml.Attribute("styleID").Value);
            if (modTextStyle != null) return modTextStyle;
            modTextStyle = new ModTextStyle();
            modTextStyle = ReadBaseStyle(textXml, modTextStyle);
            if (textXml.Attribute("rowSpace") != null)
            {
                float i = 1;
                if (float.TryParse(textXml.Attribute("rowSpace").Value, out i))
                {
                    modTextStyle.RowSpace = i;
                }
            }
            if (textXml.Attribute("fontName") != null && string.IsNullOrWhiteSpace(textXml.Attribute("fontName").Value)) modTextStyle.FontName = textXml.Attribute("fontName").Value;
            if (textXml.Attribute("fontSize") != null)
            {
                float f = 10f;
                if (float.TryParse(textXml.Attribute("fontSize").Value, out f))
                {
                    modTextStyle.FontSize = f;
                }
            }
            FontStyle fs = FontStyle.Regular;
            if (textXml.Attribute("bold") != null)
            {
                if (textXml.Attribute("bold").Value == "true" || textXml.Attribute("bold").Value == "0")
                {
                    fs |= FontStyle.Bold;
                }
            }
            if (textXml.Attribute("italic") != null)
            {
                if (textXml.Attribute("italic").Value == "true" || textXml.Attribute("italic").Value == "0")
                {
                    fs |= FontStyle.Italic;
                }
            }
            if (textXml.Attribute("strikeout") != null)
            {
                if (textXml.Attribute("strikeout").Value == "true" || textXml.Attribute("strikeout").Value == "0")
                {
                    fs |= FontStyle.Strikeout;
                }
            }
            if (textXml.Attribute("underline") != null)
            {
                if (textXml.Attribute("underline").Value == "true" || textXml.Attribute("underline").Value == "0")
                {
                    fs |= FontStyle.Underline;
                }
            }
            modTextStyle.TextFontStyle = fs;
            _lstModRegion.Add(modTextStyle);
            return modTextStyle;
        }
        static private XElement FindStyleXml(string StyleType, string xmlAttr, XElement xNow)
        {
            if (xNow.Attribute(xmlAttr) != null)
            {
                IEnumerable<XElement> ieStyle = from x in xNow.Document.Elements("report").Elements("styles") select x;
                if (ieStyle.Count() > 0 && ieStyle.ElementAt(0) != null)
                {
                    IEnumerable<XElement> ieAllStyle = from x in ieStyle.ElementAt(0).Elements(StyleType) select x;
                    if (ieAllStyle.Count() > 0)
                    {
                        return ieAllStyle.First(x => x.Attribute("styleID") != null && x.Attribute("styleID").Value == xNow.Attribute(xmlAttr).Value);
                    }
                    return null;
                }
                return null;
            }
            return null;
        }

        static private dynamic ReadCase(XElement blockXml, dynamic modCase)
        {
            modCase.Blocks = new List<ModBlock>();
            modCase = ReadBaseBlock(blockXml, modCase);
            IEnumerable<XElement> ieXml = from x in blockXml.Elements() select x;
            foreach (XElement xml in ieXml)
            {
                if (xml.Name == null) continue;
                switch (xml.Name.ToString())
                {
                    case "table":
                        modCase.Blocks.Add(ReadTable(xml));
                        break;
                    case "text":
                        modCase.Blocks.Add(ReadText(xml));
                        break;
                    case "delimiter":
                        modCase.Blocks.Add(ReadDelimiter(xml));
                        break;
                    case "image":
                        modCase.Blocks.Add(ReadImage(xml));
                        break;
                    case "block":
                        modCase.Blocks.Add(ReadCase(xml, new ModCase()));
                        break;
                    default:
                        break;
                }
            }
            return modCase;
        }
        static private dynamic ReadBaseBlock(XElement xml, dynamic dyn)
        {
            if (xml.Attribute("bindID") != null) dyn.BindID = xml.Attribute("bindID").Value;
            if (xml.Attribute("bindType") != null)
            {
                EnmBindType enmbt = EnmBindType.Inherit;
                if (Enum.TryParse(xml.Attribute("bindType").Value, out enmbt))
                {
                    dyn.BindType = enmbt;
                }
            }
            dyn.BackgroundStyle = ReadRegionStyle(FindStyleXml("regionStyle", "backgroundStyleID", xml));
            dyn.BorderStyle = ReadLineStyle(FindStyleXml("lineStyle", "borderStyleID", xml));
            if (xml.Attribute("isCustom") != null)
            {
                bool isCustom = false;
                if (bool.TryParse(xml.Attribute("isCustom").Value, out isCustom))
                {
                    dyn.IsCustom = isCustom;
                }
            }
            if (xml.Attribute("isList") != null)
            {
                bool islist = false;
                if (bool.TryParse(xml.Attribute("isList").Value, out islist))
                {
                    dyn.IsList = islist;
                }
            }
            if (xml.Attribute("interval") != null)
            {
                float interval = 0;
                if (float.TryParse(xml.Attribute("interval").Value, out interval))
                {
                    dyn.Interval = interval;
                }
            }
            if (xml.Attribute("location") != null && xml.Attribute("location").Value.Split(',').Length == 2)
            {
                float x = -1;
                float.TryParse(xml.Attribute("location").Value.Split(',')[0], out x);
                float y = -1;
                float.TryParse(xml.Attribute("location").Value.Split(',')[1], out y);
                dyn.Location = new PointF(x, y);
            }
            if (xml.Attribute("memberID") != null) dyn.MemberID = xml.Attribute("memberID").Value;
            if (xml.Attribute("position") != null)
            {
                EnmPosition enmp = EnmPosition.Suspend;
                if (Enum.TryParse(xml.Attribute("position").Value, out enmp))
                {
                    dyn.Position = enmp;
                }
            }
            if (xml.Attribute("size") != null && xml.Attribute("size").Value.Split(',').Length == 2)
            {
                float w = 0;
                float.TryParse(xml.Attribute("size").Value.Split(',')[0], out w);
                float h = 0;
                float.TryParse(xml.Attribute("size").Value.Split(',')[1], out h);
                dyn.Size = new SizeF(w, h);
            }
            return dyn;
        }
        static private dynamic ReadText(XElement textXml)
        {
            dynamic modText;
            if (textXml.Attribute("textType") != null)
            {
                switch (textXml.Attribute("textType").Value)
                {
                    case "Bind":
                        modText = new ModTextBind();
                        break;
                    case "Cal":
                        modText = new ModTextCal();
                        break;
                    default:
                        modText = new ModText();
                        break;
                }
            }
            else
            {
                modText = new ModText();
            }
            modText = ReadBaseBlock(textXml, modText);
            modText.TextStyle = ReadTextStyle(FindStyleXml("textStyle", "textStyleID", textXml));
            if (textXml.Attribute("text") != null) modText.Text = textXml.Attribute("text").Value;
            return modText;
        }
        static private ModDelimiter ReadDelimiter(XElement lineXml)
        {
            ModDelimiter modLine = new ModDelimiter();
            modLine = ReadBaseBlock(lineXml, modLine);
            modLine.LineStyle = ReadLineStyle(FindStyleXml("lineStyle", "lineStyleID", lineXml));
            if (lineXml.Attribute("autoSizeWidth") != null)
            {
                bool bRsl = false;
                if (bool.TryParse(lineXml.Attribute("autoSizeWidth").Value, out bRsl))
                {
                    modLine.AutoSizeWidth = bRsl;
                }
            }
            if (lineXml.Attribute("autoSizeHeight") != null)
            {
                bool bRsl = false;
                if (bool.TryParse(lineXml.Attribute("autoSizeHeight").Value, out bRsl))
                {
                    modLine.AutoSizeHeight = bRsl;
                }
            }
            return modLine;
        }
        static private ModImage ReadImage(XElement imgXml)
        {
            ModImage modImage = new ModImage();
            modImage = ReadBaseBlock(imgXml, modImage);
            if (imgXml.Attribute("ifScale") != null)
            {
                bool bRsl = false;
                if (bool.TryParse(imgXml.Attribute("ifScale").Value, out bRsl))
                {
                    modImage.IfScale = bRsl;
                }
            }
            return modImage;
        }

        static private ModTable ReadTable(XElement tableXml)
        {
            ModTable modTable = new ModTable();
            modTable = ReadBaseBlock(tableXml, modTable);
            if (tableXml.Attribute("tableBindType") != null)
            {
                EnmTableBindType enmtbt = EnmTableBindType.Row;
                if (Enum.TryParse(tableXml.Attribute("tableBindType").Value, out enmtbt))
                {
                    modTable.TableBindType = enmtbt;
                }
            }
            IEnumerable<XElement> ieItem = from x in tableXml.Elements("item") select x;
            if (ieItem.Count() > 0)
            {
                foreach (XElement xml in ieItem)
                {
                    modTable.Items.Add(ReadItem(xml));
                }
            }
            return modTable;
        }
        static private ModItem ReadItem(XElement itemXml)
        {
            ModItem modItem = new ModItem();
            IEnumerable<XElement> ieCell = from x in itemXml.Elements("cell") select x;
            foreach (XElement xml in ieCell)
            {
                modItem.Cells.Add(ReadCell(xml));
            }
            if (itemXml.Attribute("fixed") != null)
            {
                bool fix = false;
                if (bool.TryParse(itemXml.Attribute("fixed").Value, out fix))
                {
                    modItem.Fixed = fix;
                }
            }
            if (itemXml.Attribute("size") != null)
            {
                float i = 0;
                if (float.TryParse(itemXml.Attribute("size").Value, out i))
                {
                    modItem.Size = i;
                }
            }
            return modItem;
        }
        static private ModCell ReadCell(XElement cellXml)
        {
            ModCell modCell = new ModCell();
            modCell = ReadBaseBlock(cellXml, modCell);
            IEnumerable<XElement> ieXml = from x in cellXml.Elements() select x;
            foreach (XElement xml in ieXml)
            {
                if (xml.Name == null) continue;
                switch (xml.Name.ToString())
                {
                    case "table":
                        modCell.Blocks.Add(ReadTable(xml));
                        break;
                    case "text":
                        modCell.Blocks.Add(ReadText(xml));
                        break;
                    case "delimiter":
                        modCell.Blocks.Add(ReadDelimiter(xml));
                        break;
                    case "image":
                        modCell.Blocks.Add(ReadImage(xml));
                        break;
                    default:
                        break;
                }
            }
            if (cellXml.Attribute("rowSpan") != null)
            {
                int rs = 1;
                if (int.TryParse(cellXml.Attribute("rowSpan").Value, out rs))
                {
                    modCell.RowSpan = rs;
                }
            }
            if (cellXml.Attribute("columnSpan") != null)
            {
                int cs = 1;
                if (int.TryParse(cellXml.Attribute("columnSpan").Value, out cs))
                {
                    modCell.ColumnSpan = cs;
                }
            }
            if (cellXml.Attribute("horizontalAlign") != null)
            {
                EnmHorizontalAlign enmha = EnmHorizontalAlign.Left;
                if (Enum.TryParse(cellXml.Attribute("horizontalAlign").Value, out enmha))
                {
                    modCell.HorizontalAlign = enmha;
                }
            }
            if (cellXml.Attribute("verticalAlign") != null)
            {
                EnmVerticalAlign enmva = EnmVerticalAlign.Top;
                if (Enum.TryParse(cellXml.Attribute("verticalAlign").Value, out enmva))
                {
                    modCell.VerticalAlign = enmva;
                }
            }
            return modCell;
        }
    }
}